using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using WebApplication8.Data;
using WebApplication8.Models;
using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;

namespace WebApplication8.Controllers
{
  [Route("api/[controller]")]
  [ApiController]
  [AllowAnonymous]
  public class OauthController : Controller
  {
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly ILogger<LoginModel> _logger;

    public IConfiguration Configuration { get; }

    public OauthController(IConfiguration configuration, SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger,
      UserManager<ApplicationUser> userManager)
    {
      _signInManager = signInManager;
      _logger = logger;
      Configuration = configuration;
      _userManager = userManager;
    }

    [HttpPost("msg")]
    public async Task<IActionResult> Msg()
    {
      return Ok("ok1");
    }

    [HttpPost("msg2")]
    public async Task<IActionResult> Msg2()
    {
      return Ok(new { msg = "msg" });
    }

    [HttpGet("user")]
    public async Task<IActionResult> User1()
    {
      var IsAuthenticated = this.Request.HttpContext.User.Identity.IsAuthenticated;
      var UserName = this.Request.HttpContext.User.Identity.Name;
      return Ok(new { UserName, IsAuthenticated });
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginModel Input)
    {
      if (ModelState.IsValid)
      {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true

        var user = await _userManager.FindByNameAsync(Input.UserName);
        SignInResult result = SignInResult.Success;
        if (user == null)
          result = SignInResult.Failed;
        else if (user.TwoFactorEnabled)
          result = SignInResult.TwoFactorRequired;
        else if (result.IsLockedOut)
          result = SignInResult.LockedOut;
        else if (result.IsLockedOut)
          result = SignInResult.LockedOut;
        await _signInManager.SignInAsync(user, Input.RememberMe);
        // var result = await _signInManager.PasswordSignInAsync (Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure : true);
        if (result.Succeeded)
        {
          _logger.LogInformation("User logged in.");
          return Ok(new { success = "Ok" });
        }
        if (result.RequiresTwoFactor)
        {
          _logger.LogWarning("User account need RequiresTwoFactor.");
          return BadRequest(new { error = "RequiresTwoFactor", url = "Identity/Account/LoginWith2fa" });
        }
        if (result.IsLockedOut)
        {
          _logger.LogWarning("User account locked out.");
          return BadRequest(new { error = "IsLockedOut", url = "Identity/Account/Lockout" });
        }
        else
        {
          _logger.LogWarning("Invalid login attempt.");
          return BadRequest(new { error = "Invalid login attempt." });
        }
      }

      _logger.LogWarning("Invalid login attempt.");
      return BadRequest(new { error = "ModelState is Invalid." });
    }

    [HttpPost("authenticate")]
    public IActionResult RequestToken([FromBody] TokenRequest request)
    {
      if (request != null)
      {
        //验证账号密码,这里只是为了demo，正式场景应该是与DB之类的数据源比对
        if ("admin".Equals(request.UserName) && "123456".Equals(request.Password))
        {
          var claims = new[] {
        //加入用户的名称
        new Claim (ClaimTypes.Name, request.UserName)
          };

          var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:SecurityKey"]));
          var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

          var authTime = DateTime.UtcNow;
          var expiresAt = authTime.AddDays(7);

          var token = new JwtSecurityToken(
            issuer: "lilibuy.com",
            audience: "lilibuy.com",
            claims: claims,
            expires: expiresAt,
            signingCredentials: creds);

          return Ok(new
          {
            access_token = new JwtSecurityTokenHandler().WriteToken(token),
            token_type = "Bearer",
            profile = new
            {
              name = request.UserName,
              auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds(),
              expires_at = new DateTimeOffset(expiresAt).ToUnixTimeSeconds()
            }
          });
        }
      }

      return BadRequest("Could not verify username and password.Pls check your information.");
    }
  }
}